#include "device/DeviceManager.h"
#include "device/ModbusDevice.h"
#include "core/Logger.h"

#include <chrono>
#include <algorithm>
#include <cstdio>

namespace ems {
namespace device {

DeviceManager::DeviceManager() : running_(false) {
}

DeviceManager::~DeviceManager() {
    stop();
}

bool DeviceManager::initialize() {
    LOG_INFO("Initializing device manager...");

    if (!loadDevicesFromConfig()) {
        LOG_ERROR("Failed to load devices from configuration");
        return false;
    }

    LOG_INFO("Device manager initialized with " + std::to_string(devices_.size()) + " devices");
    return true;
}

void DeviceManager::start() {
    if (running_) {
        LOG_WARN("Device manager already running");
        return;
    }

    LOG_INFO("Starting device manager...");

    {
        std::lock_guard<std::mutex> lock(devicesMutex_);
        for (auto& pair : devices_) {
            auto& device = pair.second;
            if (device->isEnabled()) {
                if (!device->connect()) {
                    LOG_ERROR("Failed to connect device: " + device->getId());
                }
            }
        }
    }

    running_ = true;
    monitorThread_ = std::thread(&DeviceManager::monitorDevices, this);

    LOG_INFO("Device manager started successfully");
}

void DeviceManager::stop() {
    if (!running_) {
        return;
    }

    LOG_INFO("Stopping device manager...");

    running_ = false;

    if (monitorThread_.joinable()) {
        monitorThread_.join();
    }

    {
        std::lock_guard<std::mutex> lock(devicesMutex_);
        for (auto& pair : devices_) {
            pair.second->disconnect();
        }
    }

    LOG_INFO("Device manager stopped");
}

bool DeviceManager::addDevice(std::unique_ptr<DeviceBase> device) {
    if (!device) {
        LOG_ERROR("Cannot add null device");
        return false;
    }

    std::lock_guard<std::mutex> lock(devicesMutex_);

    const std::string& deviceId = device->getId();
    if (devices_.find(deviceId) != devices_.end()) {
        LOG_ERROR("Device already exists: " + deviceId);
        return false;
    }

    if (!device->initialize()) {
        LOG_ERROR("Failed to initialize device: " + deviceId);
        return false;
    }

    devices_[deviceId] = std::move(device);
    LOG_INFO("Added device: " + deviceId);
    return true;
}

bool DeviceManager::removeDevice(const std::string& deviceId) {
    std::lock_guard<std::mutex> lock(devicesMutex_);

    auto it = devices_.find(deviceId);
    if (it == devices_.end()) {
        LOG_WARN("Device not found: " + deviceId);
        return false;
    }

    it->second->disconnect();
    devices_.erase(it);

    LOG_INFO("Removed device: " + deviceId);
    return true;
}

DeviceBase* DeviceManager::getDevice(const std::string& deviceId) {
    std::lock_guard<std::mutex> lock(devicesMutex_);

    auto it = devices_.find(deviceId);
    if (it != devices_.end()) {
        return it->second.get();
    }

    return nullptr;
}

std::vector<DeviceBase*> DeviceManager::getAllDevices() {
    std::lock_guard<std::mutex> lock(devicesMutex_);

    std::vector<DeviceBase*> result;
    result.reserve(devices_.size());

    for (auto& pair : devices_) {
        result.push_back(pair.second.get());
    }

    return result;
}

std::vector<DeviceBase*> DeviceManager::getConnectedDevices() {
    std::lock_guard<std::mutex> lock(devicesMutex_);

    std::vector<DeviceBase*> result;

    for (auto& pair : devices_) {
        if (pair.second->isConnected()) {
            result.push_back(pair.second.get());
        }
    }

    return result;
}

size_t DeviceManager::getDeviceCount() const {
    std::lock_guard<std::mutex> lock(devicesMutex_);
    return devices_.size();
}

size_t DeviceManager::getConnectedDeviceCount() const {
    std::lock_guard<std::mutex> lock(devicesMutex_);

    size_t count = 0;
    for (const auto& pair : devices_) {
        if (pair.second->isConnected()) {
            count++;
        }
    }
    return count;
}

bool DeviceManager::collectAllData(std::vector<std::pair<std::string, std::vector<DataValue>>>& data) {
    data.clear();

    std::vector<DeviceBase*> connectedDevices = getConnectedDevices();

    for (auto* device : connectedDevices) {
        if (!device->isEnabled()) {
            continue;
        }

        std::vector<DataValue> deviceData;
        if (device->collectData(deviceData)) {
            if (!deviceData.empty()) {
                data.emplace_back(device->getId(), std::move(deviceData));
            }
        } else {
            LOG_ERROR("Failed to collect data from device: " + device->getId());
        }
    }

    return !data.empty();
}

void DeviceManager::monitorDevices() {
    LOG_INFO("Device monitor thread started");

    try {
        while (running_) {
            {
                std::lock_guard<std::mutex> lock(devicesMutex_);

                for (auto& pair : devices_) {
                    auto& device = pair.second;

                    if (!device->isEnabled()) {
                        continue;
                    }

                    if (!device->isConnected()) {
                        LOG_DEBUG("Attempting to reconnect device: " + device->getId());
                        if (device->connect()) {
                            LOG_INFO("Device reconnected: " + device->getId());
                        }
                    }
                }
            }

            std::this_thread::sleep_for(std::chrono::seconds(30));
        }

        LOG_INFO("Device monitor thread stopped");
    } catch (const std::system_error& e) {
        std::fprintf(stderr, "[DeviceManager::monitorDevices] system_error: %s\n", e.what());
        throw;
    }
}

bool DeviceManager::loadDevicesFromConfig() {
    auto& config = utils::Config::getInstance();
    const auto& deviceConfigs = config.getDevices();

    if (deviceConfigs.empty()) {
        LOG_WARN("No devices found in configuration");
        return true; // Empty config is valid
    }

    for (const auto& deviceConfig : deviceConfigs) {
        try {
            auto device = createDevice(deviceConfig);
            if (device) {
                if (!addDevice(std::move(device))) {
                    LOG_ERROR("Failed to add device: " + deviceConfig.id);
                    return false;
                }
            } else {
                LOG_ERROR("Failed to create device: " + deviceConfig.id);
                return false;
            }
        } catch (const std::exception& e) {
            LOG_ERROR("Exception creating device " + deviceConfig.id + ": " + e.what());
            return false;
        }
    }

    return true;
}

std::unique_ptr<DeviceBase> DeviceManager::createDevice(const utils::DeviceConfig& config) {
    LOG_INFO("Creating device: " + config.id + " (" + config.type + ")");

    // 兼容新旧协议命名：tcpmodbus/modbus_tcp 与 rtumodbus/modbus_rtu
    const std::string proto = config.protocol;
    const bool isTcpModbus = (proto == "modbus_tcp" || proto == "tcpmodbus");
    const bool isRtuModbus = (proto == "modbus_rtu" || proto == "rtumodbus");

    if (isTcpModbus || isRtuModbus) {
        // Create ModbusDevice using new operator since C++11 doesn't have make_unique
        auto device = std::unique_ptr<ModbusDevice>(new ModbusDevice(config.id, config.name, config.type));

        if (!device->setConnectionParams(config)) {
            LOG_ERROR("Failed to set connection parameters for device: " + config.id);
            return nullptr;
        }

        // Set protocol config
        if (!config.protocolConfig.empty()) {
            if (!device->setProtocolConfig(config.protocolConfig)) {
                LOG_ERROR("Failed to set protocol config for device: " + config.id);
                return nullptr;
            }
        }

        // Set device properties
        device->setEnabled(config.enabled);
        device->setPollInterval(config.pollInterval);

        return std::move(device);
    }

    LOG_ERROR("Unsupported device protocol: " + config.protocol);
    return nullptr;
}

bool DeviceManager::reloadDevices() {
    auto& config = utils::Config::getInstance();
    const auto& deviceConfigs = config.getDevices();

    std::vector<std::pair<std::string, std::unique_ptr<DeviceBase>>> newDevices;
    newDevices.reserve(deviceConfigs.size());
    std::vector<DeviceBase*> toConnect;
    toConnect.reserve(deviceConfigs.size());

    for (const auto& deviceConfig : deviceConfigs) {
        auto device = createDevice(deviceConfig);
        if (!device) {
            LOG_ERROR("Failed to create device during reload: " + deviceConfig.id);
            return false;
        }
        if (!device->initialize()) {
            LOG_ERROR("Failed to initialize device during reload: " + deviceConfig.id);
            return false;
        }
        toConnect.push_back(device.get());
        newDevices.emplace_back(deviceConfig.id, std::move(device));
    }

    {
        std::lock_guard<std::mutex> lock(devicesMutex_);
        for (auto& pair : devices_) {
            pair.second->disconnect();
        }
        devices_.clear();
        for (auto& entry : newDevices) {
            devices_.emplace(entry.first, std::move(entry.second));
        }
    }

    if (running_) {
        for (auto* device : toConnect) {
            if (device && device->isEnabled()) {
                if (!device->isConnected()) {
                    device->connect();
                }
            }
        }
    }

    LOG_INFO("DeviceManager reloaded devices from configuration, count=" << deviceConfigs.size());
    return true;
}

}  // namespace device
}  // namespace ems
